`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2024/07/11 10:36:49
// Design Name: 
// Module Name: mem
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module mem( // 访存模块
    input clk,  // 时钟
    input MEM_valid,    // 访存有效信号
    input [105:0] EXE_MEM_bus_r,    // EXE -> MEM
    input [31:0] dm_rdata,  // 防存读数据
    
    output [31:0] dm_addr,  // 访存读写地址
    output reg [3:0] dm_wen,    // 访存写使能
    output reg [31:0] dm_wdata, // 访存写数据
    output MEM_over,        // 访存结束
    output [69:0] MEM_WB_bus,   // MEM -> WB

    output [31:0] MEM_pc    // 展示 PC
    );
    
    // 访存参数
    wire [3:0] mem_control;
    wire [31:0] store_data;
    wire [31:0] alu_result;
    
    // 写回参数
    wire rf_wen;
    wire [4:0] rf_wdest;
    wire [31:0] pc;
    
    // EXE -> MEM
    assign {
        mem_control,
        store_data,
        alu_result,
        rf_wen,
        rf_wdest,
        pc} = EXE_MEM_bus_r;
    
    
    
    wire inst_load;
    wire inst_store;
    wire ls_word;   // 单位：字符、字节
    wire lb_sign;   // 有无符号
    assign {inst_load, inst_store, 
        ls_word, lb_sign} = mem_control;
    
    // 访存读写地址
    assign dm_addr = alu_result;
    
    
    always @ (*)
        begin
            if(MEM_valid && inst_store)
                begin   // if begin
                    if(ls_word) begin
                        dm_wen <= 4'b1111;
                    end
                    else begin      // else begin
                        case(dm_addr[1:0])
                            2'b00 : dm_wen <= 4'b0001;
                            2'b01 : dm_wen <= 4'b0010;
                            2'b10 : dm_wen <= 4'b0100;
                            2'b11 : dm_wen <= 4'b1000;
                            default : dm_wen <= 4'b0000;
                        endcase
                    end             // else [ls_word] end
                end     // if end
            else begin
                    dm_wen <= 4'b0000;
            end
         end     
    
    
    // store 参数
    always @ (*) begin 
            case (dm_addr[1:0])
            2'b00	: dm_wdata <= store_data;
            2'b01	: dm_wdata <= {16'd0,store_data[7:0],8'd0};
            2'b10	: dm_wdata <= {8'd0, store_data[7:0],16'd0};
            2'b11	: dm_wdata <= {store_data[7:0], 24'd0};
            default : dm_wdata <= store_data;
        endcase    
    end
    
    // Load 读取数据
    wire load_sign;
    wire [31:0] load_result;
    assign load_sign =  (dm_addr[1:0]==2'd0) ? dm_rdata[ 7] : (dm_addr[1:0]==2'd1) ? dm_rdata[15] :
                        (dm_addr[1:0]==2'd2) ? dm_rdata[23] : dm_rdata[31] ;
    assign load_result[7:0] = (dm_addr[1:0]==2'd0) ? dm_rdata[ 7:0 ] :
                                (dm_addr[1:0]==2'd1) ? dm_rdata[15:8 ] :
                                (dm_addr[1:0]==2'd2) ? dm_rdata[23:16] :
                                dm_rdata[31:24] ;
    assign load_result[31:8]= ls_word ? dm_rdata[31:8] :
                                        {24{lb_sign & load_sign}};
    
    
    reg MEM_valid_r;
    always @ (posedge clk) begin
        MEM_valid_r <= MEM_valid;
    end
    
    assign MEM_over = inst_load ? MEM_valid_r : MEM_valid;
    
    // MEM -> WB
    wire [31:0] mem_result; // 写回的数据是 ALU运算、Load加载 的结果
    assign mem_result = inst_load ? load_result : alu_result;
    assign MEM_WB_bus = {
                rf_wen, rf_wdest,   // WB 所需信号
                mem_result, // 写回的数据
                pc};    // PC
    
    // 展示 PC
    assign MEM_pc = pc;
    
endmodule
